package com.yoyo.yoyoplat.util;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;


import com.google.gson.Gson;
import com.yoyo.yoyoplat.base.ApiConstants;
import com.yoyo.yoyoplat.bean.Progress;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;


/**
 * Created by xiaoyehai on 2018/5/21 0021.
 */

public class HttpUtils {

    //线程池
    private static ExecutorService executor;
    private static Handler mHandler;

    static {
        executor = Executors.newFixedThreadPool(5);
        mHandler = new Handler();
    }

    private static HttpURLConnection getConnection(String url, String method) throws IOException {
        return getConnection(url, method, false);
    }

    private static HttpURLConnection getConnection(String url, String method, boolean isJson) throws IOException {
//                    URL u = new URL(url);
//                    connection = (HttpURLConnection) u.openConnection();
        HttpURLConnection connection = connect(url);
        // 设置输入可用
        connection.setDoInput(true);
        // 设置输出可用
        connection.setDoOutput(true);
        // 设置请求方式
        connection.setRequestMethod(method);
        // 设置连接超时
        connection.setConnectTimeout(5000);
        if (isJson) {
            //设置请求参数为json
            connection.setRequestProperty("Content-Type", "application/json");
        }
        // 设置读取超时
        connection.setReadTimeout(5000);
        // 设置缓存不可用
        connection.setUseCaches(false);
        // 开始连接
        connection.connect();
        return connection;
    }

    /**
     * 执行网络请求操作,返回数据是对象
     *
     * @param method 请求方式(需要传入String类型的参数:"GET","POST")
     * @param url    请求的url
     */
    public static <T> void doHttpRequest(final String method, final String url, final String urlParams,
                                         final String bodyParams, final boolean isJson, final Class<T> cls, final ObjectCallback<T> callback) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;
                OutputStream outputStream = null;
                try {
                    String urlTemp = url;
                    if (!StringUtils.isNull(urlParams) &&
                            ((method.equals(ApiConstants.POST) && isJson) || method.equals(ApiConstants.GET))) {
                        urlTemp = url.concat("?").concat(urlParams);
                    }
                    connection = getConnection(urlTemp, method, isJson);
                    if (method.equals(ApiConstants.POST) && !StringUtils.isNull(bodyParams)) {
                        // 只有当POST请求时才会执行此代码段
                        outputStream = connection.getOutputStream();
                        outputStream.write(bodyParams.getBytes());
                        outputStream.flush();
                    }
                    int responseCode = connection.getResponseCode();
                    if (responseCode == 200) {
                        InputStream inputStream = connection.getInputStream();
                        String result = inputStream2String(inputStream);
                        LogUtil.e("请求数据成功：" + result);
                        if (result != null && callback != null) {
                            postSuccessObject(callback, new Gson().fromJson(result, cls));
                        }
                    } else {
                        if (callback != null) {
                            postFailed(callback, responseCode, new Exception("请求数据失败：" + responseCode));
                        }
                    }

                } catch (final Exception e) {
                    e.printStackTrace();
                    if (callback != null) {
                        postFailed(callback, 0, e);
                    }

                } finally {
                    if (connection != null) {
                        connection.disconnect();
                    }
                    if (outputStream != null) {
                        try {
                            outputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
    }

    public static <T> void doHttpRequest(final String method, final String url,
                                         final Map<String, String> params, final Class<T> cls, final ObjectCallback<T> callback) {
        doHttpRequest(method, url, params, null, false, cls, callback);
    }

    public static <T> void doHttpRequest(final String method, final String url,
                                         final Map<String, String> params, final Object o, final Class<T> cls, final ObjectCallback<T> callback) {
        doHttpRequest(method, url, params, o, true, cls, callback);
    }

    public static <T> void doHttpRequest(final String method, final String url,
                                         final Map<String, String> params,
                                         final Object o, final boolean isJson, final Class<T> cls, final ObjectCallback<T> callback) {

        executor.execute(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;
                OutputStream outputStream = null;
                try {
                    String urlTemp = url;
                    if (params != null && !params.isEmpty() &&
                            ((method.equals(ApiConstants.POST) && isJson) || method.equals(ApiConstants.GET))) {
                        StringBuilder sb = map2KeyValue(params);
                        urlTemp = url.concat("?").concat(sb.substring(0, sb.length() - 1));
                    }
                    connection = getConnection(urlTemp, method, isJson);
                    if (method.equals(ApiConstants.POST)) {
                        // 只有当POST请求时才会执行此代码段
                        if (isJson) {
                            if (o != null) {
                                outputStream = connection.getOutputStream();
                                outputStream.write(new Gson().toJson(o).getBytes());
                                outputStream.flush();
                            }
                        } else {
                            if (params != null && !params.isEmpty()) {
                                outputStream = connection.getOutputStream();
                                StringBuilder sb = map2KeyValue(params);
                                outputStream.write(sb.substring(0, sb.length() - 1).getBytes());
                                outputStream.flush();
                            } else if (o != null) {
                                outputStream = connection.getOutputStream();
                                outputStream.write(parseURLPair(o, new StringBuilder()).getBytes());
                                outputStream.flush();
                            }
                        }
                    }
                    int responseCode = connection.getResponseCode();
                    if (responseCode == 200) {
                        InputStream inputStream = connection.getInputStream();
                        String result = inputStream2String(inputStream);
                        LogUtil.e("请求数据成功：" + result);
                        if (result != null && callback != null) {
                            postSuccessObject(callback, new Gson().fromJson(result, cls));
                        }
                    } else {
                        if (callback != null) {
                            postFailed(callback, responseCode, new Exception("请求数据失败：" + responseCode));
                        }
                    }

                } catch (final Exception e) {
                    e.printStackTrace();
                    if (callback != null) {
                        postFailed(callback, 0, e);
                    }

                } finally {
                    if (connection != null) {
                        connection.disconnect();
                    }
                    if (outputStream != null) {
                        try {
                            outputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
    }

    public static StringBuilder map2KeyValue(Map<String, String> params) {
        StringBuilder sb = new StringBuilder();
        Set<Map.Entry<String, String>> sets = params.entrySet();
        // 将Hashmap转换为string
        for (Map.Entry<String, String> entry : sets) {
            sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }
        return sb;
    }

    private static OutputStream getOutputStream(HttpURLConnection connection, Map<String, String> params) throws IOException {
        return getOutputStream(connection, params, null);
    }

    private static OutputStream getOutputStream(HttpURLConnection connection, Map<String, String> params, Object o) throws IOException {
        // 获取输出流,connection.getOutputStream已经包含了connect方法的调用
        OutputStream outputStream = connection.getOutputStream();
        StringBuilder sb = map2KeyValue(params);
        String param;
        if (o != null) {
            try {
                param = parseURLPair(o, sb);
            } catch (Exception e) {
                param = sb.substring(0, sb.length() - 1);
            }
        } else {
            param = sb.substring(0, sb.length() - 1);
        }
        // 使用输出流将string类型的参数写到服务器
        outputStream.write(param.getBytes());
        outputStream.flush();
        return outputStream;
    }

    private static String parseURLPair(Object o, StringBuilder sb) throws Exception {
        Class<?> c = o.getClass();
        Field[] fields = c.getDeclaredFields();
        Map<String, Object> map = new TreeMap<>();
        for (Field field : fields) {
            field.setAccessible(true);
            String name = field.getName();
            Object value = field.get(o);
            if (value != null)
                map.put(name, value);
        }
        Set<Map.Entry<String, Object>> set = map.entrySet();
        for (Map.Entry<String, Object> e : set) {
            sb.append(e.getKey()).append("=").append(e.getValue()).append("&");
        }
        return sb.deleteCharAt(sb.length() - 1).toString();
    }

    /**
     * 实例化对象
     */
    private static HttpURLConnection connect(String url) throws IOException {
        if (url.startsWith("https:")) {
            try {
                HttpsURLConnection httpsURLConnection;
                TrustManager[] var2 = new TrustManager[]{new MyTrustManager()};
                SSLContext var3 = SSLContext.getInstance("TLSv1");
                var3.init(null, var2, new SecureRandom());
                SSLSocketFactory var4 = var3.getSocketFactory();
                httpsURLConnection = (HttpsURLConnection) (new URL(url)).openConnection();
                httpsURLConnection.setSSLSocketFactory(var4);
                return httpsURLConnection;
            } catch (KeyManagementException var5) {
                var5.printStackTrace();
            } catch (NoSuchAlgorithmException var6) {
                var6.printStackTrace();
            } catch (Exception var7) {
                var7.printStackTrace();
            }
        }
        return (HttpURLConnection) new URL(url).openConnection();
    }


    private static void postSuccessString(final StringCallback callback, final String result) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                callback.onSuccess(result);
            }
        });
    }

    private static void postSuccessBitmap(final Callback callback, final Bitmap bitmap) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                BitmapCallback bitmapCallback = (BitmapCallback) callback;
                bitmapCallback.onSuccess(bitmap);
            }
        });
    }

    private static void postSuccessByte(final Callback callback, final byte[] bytes) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                ByteArrayCallback byteArrayCallback = (ByteArrayCallback) callback;
                byteArrayCallback.onSuccess(bytes);
            }
        });
    }

    private static <T> void postSuccessObject(final ObjectCallback<T> callback, final T t) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                callback.onSuccess(t);
            }
        });
    }

    private static void postSuccessFile(final Callback callback, final File file) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                FileCallback fileCallback = (FileCallback) callback;
                fileCallback.onComplete(file);
            }
        });
    }

    private static void postProgressFile(final Callback callback, final Progress progress) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                FileCallback fileCallback = (FileCallback) callback;
                fileCallback.onProcess(progress);
            }
        });
    }

    private static void postFailed(final Callback callback, final int code, final Exception e) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                callback.onFailure(code, e);
            }
        });
    }

    /**
     * 字节流转换成字符串
     *
     * @param inputStream 字节流
     */
    private static String inputStream2String(InputStream inputStream) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] bytes = new byte[1024];
        int len;
        try {
            while ((len = inputStream.read(bytes)) != -1) {
                baos.write(bytes, 0, len);
            }
            return new String(baos.toByteArray());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            try {
                baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 字节流转换成字节数组
     *
     * @param inputStream 输入流
     */
    private static byte[] inputStream2ByteArray(InputStream inputStream) {
        byte[] result = null;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        // 缓冲区
        byte[] bytes = new byte[1024];
        int len;
        try {
            // 使用字节数据输出流来保存数据
            while ((len = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, len);
            }
            result = outputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    public interface Callback {
        void onFailure(int code, Exception e);
    }

    public interface StringCallback extends Callback {
        void onSuccess(String result);
    }

    public interface BitmapCallback extends Callback {
        void onSuccess(Bitmap bitmap);
    }

    public interface ByteArrayCallback extends Callback {
        void onSuccess(byte[] bytes);
    }

    public interface ObjectCallback<T> extends Callback {
        void onSuccess(T t);
    }

    public interface FileCallback extends Callback {
        void onComplete(File file);

        void onProcess(Progress progress);
    }
}